#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <Eigen/Eigen>
#include <iostream>
#include <opencv2/opencv.hpp>

constexpr double MY_PI = 3.1415926;

Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
    Eigen::Matrix4f view = Eigen::Matrix4f::Identity();

	// 平移
    Eigen::Matrix4f translate;
    translate << 1, 0, 0, -eye_pos[0], 
				 0, 1, 0, -eye_pos[1], 
				 0, 0, 1,-eye_pos[2], 
				 0, 0, 0, 1;

    view = translate * view;

    return view;
}

// 实现三维中绕Z轴旋转 rotation_angle 角度
//（本地空间 -> 世界空间）
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.
	// 旋转矩阵
	Eigen::Matrix4f M_rotation;
	float angle = rotation_angle * MY_PI / 180.0;
	M_rotation << cos(angle),0,-sin(angle),0,
			0,1,0,0,
			sin(angle),0,cos(angle),0,
			0,0,0,1;

	model = M_rotation * model;
    return model;
}

// 使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵
// 视图空间到屏幕空间
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    // Students will implement this function

    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the projection matrix for the given parameters.
    // Then return it.
 	Eigen::Matrix4f M_persp2ortho(4,4);
	Eigen::Matrix4f M_scale(4,4);
	Eigen::Matrix4f M_trans(4,4);

	float angle = eye_fov * MY_PI / 180.0;
	// 屏幕高度
	float height = zNear * tan(angle) * 2; 
	// 屏幕宽度
	float width = height * aspect_ratio;

	// 假设近裁剪面 点 (r,t,n)
	auto t = -zNear * tan(angle/2);
	auto r = t * aspect_ratio;
	auto l = -r;
	auto b = -t;

    // 缩放
	Eigen::Matrix4f translate;
    translate << 1.0 / tan(eye_fov / 2), 0, 0, 0,
				 0, 1.0 / tan(eye_fov / 2), 0, 0, 
				 0, 0, (zFar + zNear) / (zNear - zFar), 2 * zFar * zNear / (zNear - zFar), 
				 0, 0, -1, 0;

	projection = translate * projection;

    return projection;
}

int main(int argc, const char** argv)
{
    float angle = 0;
    bool command_line = false;
    std::string filename = "output.png";

    if (argc >= 3) {
        command_line = true;
        angle = std::stof(argv[2]); // -r by default
        if (argc == 4) {
            filename = std::string(argv[3]);
        }
    }

    rst::rasterizer r(700, 700);

    Eigen::Vector3f eye_pos = {0, 0, 5};

    std::vector<Eigen::Vector3f> pos{{2, 0, -2}, {0, 2, -2}, {-2, 0, -2}};

    std::vector<Eigen::Vector3i> ind{{0, 1, 2}};

    auto pos_id = r.load_positions(pos);
    auto ind_id = r.load_indices(ind);

    int key = 0;
    int frame_count = 0;

    if (command_line) {
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        r.set_projection(get_projection_matrix(45, 1, 0.1, 50));

        r.draw(pos_id, ind_id, rst::Primitive::Triangle);
        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);

        cv::imwrite(filename, image);

        return 0;
    }

    while (key != 27) {
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        r.set_projection(get_projection_matrix(45, 1, 0.1, 50));

        r.draw(pos_id, ind_id, rst::Primitive::Triangle);

        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);
        cv::imshow("image", image);
        key = cv::waitKey(10);

        std::cout << "frame count: " << frame_count++ << '\n';

        if (key == 'a') {
            angle += 10;
        }
        else if (key == 'd') {
            angle -= 10;
        }
    }

    return 0;
}
